/**
 * 
 */
package org.swing.utility.common.jmx;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.ReflectionException;

/**
 * @author lqnhu
 *
 */
public class FieldBasedDynamicMBean extends Observable implements DynamicMBean {
	protected boolean readonly;
	protected Map<String, Field> fields;

	protected FieldBasedDynamicMBean() {
		this(true);
	}

	protected FieldBasedDynamicMBean(boolean readonly) {
		this.readonly = readonly;
		this.fields = findFields();
	}

	public Object getAttribute(String att) throws AttributeNotFoundException,
			MBeanException, ReflectionException {
		Field f = fields.get(att);
		if (f == null) {
			throw new AttributeNotFoundException(att);
		}
		try {
			return f.get(this);
		} catch (Exception e) {
			throw new ReflectionException(e, "error getting attribute " + att);
		}
	}

	public AttributeList getAttributes(String[] atts) {
		AttributeList list = new AttributeList();
		for (String att : atts) {
			try {
				list.add(new Attribute(att, getAttribute(att)));
			} catch (Exception e) {
			}
		}
		return list;
	}

	public MBeanInfo getMBeanInfo() {
		List<MBeanAttributeInfo> atts = new LinkedList<MBeanAttributeInfo>();
		for (Field field : fields.values()) {
			MBeanAttributeInfo att = new MBeanAttributeInfo(field.getName(),
					field.getType().getName(), null, true,
					!(readonly || Modifier.isFinal(field.getModifiers())),
					field.getType() == Boolean.TYPE, null);
			atts.add(att);
		}
		return new MBeanInfo(getClass().getName(), null,
				atts.toArray(new MBeanAttributeInfo[atts.size()]),
				new MBeanConstructorInfo[0], new MBeanOperationInfo[0],
				new MBeanNotificationInfo[0]);
	}

	public Object invoke(String actionName, Object[] params, String[] signature)
			throws MBeanException, ReflectionException {
		throw new UnsupportedOperationException(actionName);
	}

	public void setAttribute(Attribute att) throws AttributeNotFoundException,
			InvalidAttributeValueException, MBeanException, ReflectionException {
		Field f = fields.get(att.getName());
		if (f == null) {
			throw new AttributeNotFoundException(att.getName());
		}
		if (readonly || Modifier.isFinal(f.getModifiers())) {
			throw new UnsupportedOperationException(att.getName()
					+ " is read-only");
		}
		try {
			f.set(this, att.getValue());
			notifyObservers(f.getName());
		} catch (Exception e) {
			throw new ReflectionException(e, "error setting attribute " + att);
		}
	}

	public AttributeList setAttributes(AttributeList atts) {
		if (readonly) {
			return new AttributeList();
		}
		List<String> names = new LinkedList<String>();
		for (Object att : atts) {
			try {
				setAttribute((Attribute) att);
				names.add(((Attribute) att).getName());
			} catch (Exception e) {
			}
		}
		return getAttributes(names.toArray(new String[names.size()]));
	}

	protected Map<String, Field> findFields() {
		Map<String, Field> map = new HashMap<String, Field>();
		for (Field f : getClass().getFields()) {
			int m = f.getModifiers();
			if (Modifier.isPublic(m) && !Modifier.isStatic(m)) {
				map.put(f.getName(), f);
			}
		}
		return map;
	}
}
